#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include "image.h"
#include "coordconv.h"

XImage* img = NULL;
XImage* img_zoom1 = NULL;
XImage* img_zoom4 = NULL;
int zoom = 4;

int src_x = 100;
int src_y = 700;
const int map_w = 1920;
const int map_h = 900; 
double mppx_x = 0;
double mppx_y = 0;

double rot_x = 0;
double rot_y = 0;

int utm_x[3] = { 0, 0, 0 };
int utm_y[3] = { 0, 0, 0 };
int marker_x[3] = { -1, -1, -1 };
int marker_y[3] = { -1, -1, -1 };

int cross_x = 300;
int cross_y = 300;

double zero_utm_x = 0;
double zero_utm_y = 0;

int draw_grid = 0;
struct coord_convertor* cconv = NULL;
char base_name[1024];

static void
write_tfw(double x, double y)
{
	char fname[1024];
	sprintf(fname, "%s.tfw", base_name);

	FILE* f = fopen(fname, "wt");
	fprintf(f, "%f\n", mppx_x);
	fprintf(f, "%f\n", rot_x);
	fprintf(f, "%f\n", rot_y);
	fprintf(f, "%f\n", mppx_y);
	fprintf(f, "%f\n", x);
	fprintf(f, "%f\n", y);
	fclose(f);

	int i;
	sprintf(fname, "%s.markers", base_name);
	f = fopen(fname, "wt");
	for (i = 0; i < 3; i++)
	{
		fprintf(f, "marker %d: utm: %d,%d, screen: %d,%d\n",
			i + 1, utm_x[i], utm_y[i], marker_x[i], marker_y[i]);
	}
	fclose(f);
}

static void
load_markers()
{
	char fname[1024];
	sprintf(fname, "%s.markers", base_name);

	FILE* f = fopen(fname, "rt");
	if (f == NULL)
		return;

	int n;
	n = fscanf(f, "marker 1: utm: %d,%d, screen: %d,%d\n", 
		&utm_x[0], &utm_y[0], 
		&marker_x[0], &marker_y[0]);

	n = fscanf(f, "marker 2: utm: %d,%d, screen: %d,%d\n", 
		&utm_x[1], &utm_y[1], 
		&marker_x[1], &marker_y[1]);

	n = fscanf(f, "marker 3: utm: %d,%d, screen: %d,%d\n", 
		&utm_x[2], &utm_y[2], 
		&marker_x[2], &marker_y[2]);

	fclose(f);
}

static void
expose(Display* d, Window w)
{
	int n;
	char s[1024];

	GC gc = DefaultGC(d, 0);

	XPutImage(d, w, gc, img, src_x / zoom, src_y / zoom, 0, 0, map_w, map_h);
	XSetForeground(d, gc, 0x00ff0000); // red
	XSetLineAttributes(d, gc, 2, LineSolid, CapButt, JoinBevel);
	XDrawLine(d, w, gc, 300, 0, 300, map_h);
	XDrawLine(d, w, gc, 0, 300, map_w, 300);

	XSetForeground(d, gc, 0);
	XFillRectangle(d, w, gc, 0, map_h, map_w, map_h + 300);
	XSetForeground(d, gc, 0x00808080);

	n = sprintf(s, "cross x,y: %d,%d", cross_x, cross_y);
	XDrawString(d, w, gc, 20, map_h + 40, s, n);

	int i;
	for (i = 0; i < 3; i++)
	{	
		n = sprintf(s, "marker %d: utm: %d,%d, screen: %d,%d", i + 1,
			utm_x[i], utm_y[i], marker_x[i], marker_y[i]) ;
		XDrawString(d, w, gc, 20, map_h + 60 + i * 12, s, n);
	}
	
	if (marker_x[0] > 0)
	{
		double utm_dx = (double)utm_x[1] - (double)utm_x[0];
		double utm_dy = (double)utm_y[1] - (double)utm_y[0];
		double utm_dist = sqrt((utm_dx * utm_dx) + (utm_dy * utm_dy));

		double px_dx = (double)marker_x[1] - (double)marker_x[0];
		double px_dy = (double)marker_y[1] - (double)marker_y[0];
		double px_dist = sqrt((px_dx * px_dx) + (px_dy * px_dy));

		mppx_x = utm_dist / px_dist;
		
		double rot_x = mppx_x * px_dy / px_dx;	

		utm_dx = (double)utm_x[1] - (double)utm_x[2];
		utm_dy = (double)utm_y[1] - (double)utm_y[2];
		utm_dist = sqrt((utm_dx * utm_dx) + (utm_dy * utm_dy));

		px_dx = (double)marker_x[1] - (double)marker_x[2];
		px_dy = (double)marker_y[1] - (double)marker_y[2];
		px_dist = sqrt((px_dx * px_dx) + (px_dy * px_dy));

		mppx_y = -(utm_dist / px_dist);	
		
		rot_y = mppx_y * px_dx / px_dy;	

		zero_utm_x = (double)utm_x[0] - ((double)marker_x[0] / (double)zoom) * mppx_x;
		zero_utm_y = (double)utm_y[0] - ((double)marker_y[0] / (double)zoom) * mppx_y;

		n = sprintf(s, "A (pixel size x): %f", mppx_x);
		XDrawString(d, w, gc, 500, map_h + 40, s, n);
		n = sprintf(s, "B (rot x):         %f", rot_x);
		XDrawString(d, w, gc, 500, map_h + 52, s, n);
		n = sprintf(s, "C (upper left x):  %f", zero_utm_x);
		XDrawString(d, w, gc, 500, map_h + 64, s, n);
		n = sprintf(s, "D (rot y):         %f", rot_y);
		XDrawString(d, w, gc, 500, map_h + 76, s, n);
		n = sprintf(s, "E (pixel size y):  %f", mppx_y);
		XDrawString(d, w, gc, 500, map_h + 88, s, n);
		n = sprintf(s, "F (upper left y):  %f", zero_utm_y);
		XDrawString(d, w, gc, 500, map_h + 100, s, n);
	}

	XSetForeground(d, gc, 0x00CC0000);
	XSetLineAttributes(d, gc, 5, LineSolid, CapButt, JoinBevel);
	for (i = 0; i < 3; i++)
	{
		XDrawRectangle(d, w, gc, 
			(marker_x[i] - src_x - 20) / zoom, (marker_y[i] - src_y - 20) / zoom, 
			40 / zoom, 40 / zoom);
	}

	if (cconv != NULL)
	{
		XSetForeground(d, gc, 0x0000CC00);
		struct node n;
		int i;
		for (i = 0; i < 3; i++)
		{
			n.utm_lon = utm_x[i];
			n.utm_lat = utm_y[i];
			coordconv_calc_pixels(cconv, &n);
			XDrawRectangle(d, w, gc, 
				(n.x - src_x - 10) / zoom, (n.y - src_y - 10) / zoom, 
				20 / zoom, 20 / zoom);
		}
	}
	
	n = sprintf(s, "img:  %dx%d, rowbytes: %d", img->width, img->height, img->bytes_per_line);
	XDrawString(d, w, gc, 200, map_h + 40, s, n);

}

static void
key_press(Display* d, KeySym key)
{
	switch (key)
	{
	case XK_Home:
		src_x -= 100;
		break;
	case XK_End:
		src_x += 100;
		break;
	case XK_Page_Down:
		src_y += 100;
		break;
	case XK_Page_Up:
		src_y -= 100;
		break;
	case XK_Left:
		src_x -= 1;
		break;
	case XK_Right:
		src_x += 1;
		break;
	case XK_Down:
		src_y += 1;
		break;
	case XK_Up:
		src_y -= 1;
		break;
	case XK_w:
		write_tfw(zero_utm_x, zero_utm_y);
		cconv = coordconv_create();
		coordconv_load_tfw(cconv, "test.tfw");
		break;
	case XK_r:
		cconv = coordconv_create();
		coordconv_load_tfw(cconv, "test.tfw");
		load_markers();
		break;
	case XK_o:
		img = img_zoom4;
		zoom = 4;
		break;
	case XK_i:
		img = img_zoom1;
		zoom = 1;
		break;
	case XK_1:
		marker_x[0] = cross_x;
		marker_y[0] = cross_y;
		break;
	case XK_2:
		marker_x[1] = cross_x;
		marker_y[1] = cross_y;
		break;
	case XK_3:
		marker_x[2] = cross_x;
		marker_y[2] = cross_y;
		break;
	}
}

static void
process_event(Display *display, Window window)
{
	XEvent ev;
	XEvent exppp;
	KeySym key;
	XNextEvent(display, &ev);
	switch(ev.type)
	{
	case Expose:
		expose(display, window);
		break;
	case KeyPress:
		key = XLookupKeysym(&ev.xkey, 0);
		if (key == XK_q || key == XK_Return)
			exit(0);

		key_press(display, key);

		cross_x = src_x + 300;
		cross_y = src_y + 300;

	        memset(&exppp, 0, sizeof(exppp));
        	exppp.type = Expose;
	        exppp.xexpose.window = window;
        	XSendEvent(display, window, False, ExposureMask, &exppp);
		break;
	}
}

int main(int argc, char **argv)
{
	if (argc < 2)
	{
		printf("Usage: %s FILENAME\n", argv[0]);
		return 1;
	}

	strcpy(base_name, argv[1]);
	char* s = strrchr(base_name, '.');
	if (s != NULL)
		*s = 0;

	int width = 512, height = 512;
	Display *display = XOpenDisplay(NULL);
	Visual *visual = DefaultVisual(display, 0);
	Window window = XCreateSimpleWindow(display, RootWindow(display, 0), 0, 0, width, height, 1, 0, 0);

	char fname[1024];

	sprintf(fname, "%s.png", base_name);
	int rc = pic_load_png(fname, display, visual, &img_zoom1);

	sprintf(fname, "%s-4.png", base_name);
	rc = pic_load_png(fname, display, visual, &img_zoom4);

	img = img_zoom4;

	XSelectInput(display, window, ButtonPressMask|ExposureMask|KeyPressMask);
	XMapWindow(display, window);
	while(1)
	{
		process_event(display, window);
	}
}

